package com.cjlgb.design.common.security.handler;

import com.cjlgb.design.common.security.exception.ForbiddenException;
import com.cjlgb.design.common.security.exception.InvalidException;
import com.cjlgb.design.common.security.exception.ServerErrorException;
import com.cjlgb.design.common.security.exception.UnauthorizedException;
import lombok.SneakyThrows;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.HttpRequestMethodNotSupportedException;

/**
 * @author WFT
 * @date 2019/7/22
 * description: 重写OAuth异常处理的默认实现
 */
public class ResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    @Override
    @SneakyThrows
    public ResponseEntity<OAuth2Exception> translate(Exception e) {

        // 尝试从 StackTrace 中提取 SpringSecurityException
        Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);

        //  不支持的认证类型
        Exception ase = (UnsupportedGrantTypeException) throwableAnalyzer.getFirstThrowableOfType(UnsupportedGrantTypeException.class,causeChain);
        if (null != ase){
            return handleOauth2Exception(new InvalidException(e.getMessage()));
        }

        //  用户名或密码错误异常
        ase = (InvalidGrantException) throwableAnalyzer.getFirstThrowableOfType(InvalidGrantException.class, causeChain);
        if (null != ase){
            return handleOauth2Exception(new InvalidException(e.getMessage()));
        }

        //  未经授权异常
        ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
        if (null != ase){
            return handleOauth2Exception(new UnauthorizedException(e.getMessage()));
        }

        //  权限不足异常
        ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (null != ase){
            return handleOauth2Exception(new ForbiddenException(ase.getMessage()));
        }
        ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
        if (null != ase){
            return handleOauth2Exception(new ForbiddenException(ase.getMessage()));
        }

        return handleOauth2Exception(new ServerErrorException(e.getMessage()));

    }


    @SneakyThrows
    private ResponseEntity<OAuth2Exception> handleOauth2Exception(OAuth2Exception e){

        int status = e.getHttpErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
            headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
        }

        return new ResponseEntity<>(e, headers, HttpStatus.valueOf(status));
    }

}
